package com.google.code.ptrends.excel;

import static org.apache.poi.ss.usermodel.Cell.CELL_TYPE_NUMERIC;
import static org.apache.poi.ss.usermodel.Cell.CELL_TYPE_STRING;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

//import org.apache.log4j.Logger;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.ss.formula.eval.NotImplementedException;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;

import com.google.code.ptrends.excel.caching.CellStylePool;
import com.google.code.ptrends.excel.entities.Cell;
import com.google.code.ptrends.excel.entities.Row;
import com.google.code.ptrends.excel.entities.RowsGroup;
import com.google.code.ptrends.excel.entities.Worksheet;

public class POIReader implements ExcelReader {

	private final InputStream fileStream;
	private Workbook wb;
	//private static final Logger LOG = Logger.getLogger(POIReader.class);

	/**
	 * Default constructor. Creates reader form file.
	 * 
	 * @param filename
	 *            Name of file to create reader from.
	 * @throws Exception
	 */
	public POIReader(final String filename) throws FileNotFoundException,
			InvalidFormatException, IOException {
		this(new FileInputStream(filename));
	}

	/**
	 * Creates reader from stream.
	 * 
	 * @param inputstream
	 *            Stream to create reader from.
	 * @throws InvalidFormatException
	 * @throws IOException
	 */
	public POIReader(final InputStream inputstream) throws IOException,
			InvalidFormatException {
		this.fileStream = inputstream;
		initialize();
	}

	/**
	 * Initializes object`s fields and prepares it for work.
	 * 
	 * @throws Exception
	 */
	private void initialize() throws InvalidFormatException, IOException {
		this.wb = WorkbookFactory.create(fileStream);
	}

	/**
	 * @return Index of first row in a sheet specified by {@link Worksheet}
	 * @param sheet
	 *            {@link Worksheet} object
	 */
	public int getFirstRowIndex(final Worksheet sheet) {
		final Sheet tempSheet = wb.getSheet(sheet.getName());
		return tempSheet.getFirstRowNum();
	}

	/**
	 * @return Index of last row in a sheet specified by {@link Worksheet}
	 * @param sheet
	 *            {@link Worksheet} object
	 */
	public int getLastRowIndex(final Worksheet sheet) {
		final Sheet s = wb.getSheet(sheet.getName());
		return s.getLastRowNum();
	}

	/**
	 * @param sheet
	 *            {@link Worksheet} object, in which groups of rows will be
	 *            searched.
	 * @param rowsGroup
	 *            parent {@link RowsGroup} object. If not null search will be
	 *            performed within parent row group. If null search will be
	 *            performed through the whole document.
	 * @return array of {@link RowsGroup} objects specified by parameters.
	 * @throws NotImplementedException.
	 */
	public RowsGroup[] getGroups(final Worksheet sheet,
			final RowsGroup rowsGroup) {
		// TODO
		throw new NotImplementedException("this method is not implemented yet.");
	}

	/**
	 * @param sheet
	 *            {@link Worksheet} object
	 * @param index
	 *            index of a row in a sheet.
	 * @return {@link Row} object, with cells or null if there are no cells in a
	 *         row. Also null will b returned if no such sheet in a workbook.
	 */
	public Row getRow(final Worksheet sheet, final int index) {
		org.apache.poi.ss.usermodel.Sheet poiSheet = null;
		org.apache.poi.ss.usermodel.Row poiRow = null;
		CellStyle poiCellStyle = null;
		org.apache.poi.ss.usermodel.Font poiFont = null;

		poiSheet = wb.getSheet(sheet.getName());
		if (poiSheet == null) {
			return null;
		}
		poiRow = poiSheet.getRow(index);
		if (poiRow == null) {
			return null;
		}

		final List<Cell> cells = new ArrayList<Cell>();
		Cell cell = null;

		for (org.apache.poi.ss.usermodel.Cell poiCell : poiRow) {
			cell = new com.google.code.ptrends.excel.entities.Cell();
			cell.setIndex(poiCell.getColumnIndex());
			poiCellStyle = poiCell.getCellStyle();
			poiFont = wb.getFontAt(poiCellStyle.getFontIndex());
			cell.setStyle(CellStylePool.getCellStyle("FFFFFF", "000000",
					poiFont.getFontHeightInPoints(),
					poiFont.getBoldweight() == Font.BOLDWEIGHT_BOLD,
					poiFont.getItalic(), poiFont.getUnderline() != Font.U_NONE));
			switch (poiCell.getCellType()) {
			case CELL_TYPE_NUMERIC:
				cell.setValue(Double.toString(poiCell.getNumericCellValue()));
				break;
			case CELL_TYPE_STRING:
				cell.setValue(poiCell.getStringCellValue());
				break;
			default:
				cell.setValue(null);
				break;
			}
			cells.add(cell);
		}
		final Row row = new Row();
		row.setCells(cells);
		row.setIndex(index);
		return row;
	}

	/**
	 * @return Returns array of {@link Worksheet} objects. If no sheets in book
	 *         returns null.
	 */
	public Worksheet[] getWorksheets() {
		final List<Worksheet> sheets = new ArrayList<Worksheet>();
		final int num = wb.getNumberOfSheets();
		if (num == 0) {
			return new Worksheet[] {};
		}
		for (int i = 0; i < num; i++) {
			sheets.add(new Worksheet(wb.getSheetName(i)));
		}
		return sheets.toArray(new Worksheet[sheets.size()]);
	}
}
